/**
  ******************************************************************************
  * @author     Chris
  * @since      2023/10/16 22:03
  *
  * @file       sd_i2c.c
  * @brief      Xxx Hardware Driver.
  *
  * @note       This file contains the hardware driver for the Xxx.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date           Author       Notes
  *   2023/10/16     Chris        the first version
  *
  ******************************************************************************
  */

#include "stm32g4xx.h"
#include "stm32g4xx_ll_bus.h"
#include "stm32g4xx_ll_rcc.h"
#include "stm32g4xx_ll_gpio.h"
#include "stm32g4xx_ll_i2c.h"

/** region Declaration and Definition Region */

/** region Variables and Constants Region */

/** endregion Variables and Constants Region */

/** region Function Declaration Region */

/** endregion Function Declaration Region */

/** endregion Declaration and Definition Region */


/** region Public Definition Region */

/**
  * @brief  I2C2 初始化
  *
  * @note   STM32G4xx引脚位置：
  *         |      | AF4           |
  *         +------+---------------+
  *         | SCL1 | PA13/PA15/PB8 |
  *         | SDA1 | PA14/PB7 /PB9 |
  *         +------+---------------+
  *         | SCL2 | PA9/PC4       |
  *         | SDA2 | PA8/PF0       |
  *         +------+---------------+
  *         | SCL3 | PA8/PC8       |
  *         | SDA3 | PB5/PC9/PC11  |
  *         +------+---------------+
  * @retval 无
  */
void PD_H_I2C1_Init(void) {
    // 开启 I2C 时钟
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
    // 开启 GPIO 时钟
    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
    // 设置 I2C 时钟源
    LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);

    // GPIO 初始化：浮空开漏
    LL_GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Pin = LL_GPIO_PIN_9;                    // SDA
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;          // 复用模式
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;  // 开漏模式
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;               // 复用功能 4
    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = LL_GPIO_PIN_15;                   // SCL
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // I2C 初始化
    LL_I2C_InitTypeDef I2C_InitStruct;
    I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;        // I2C 模式
    I2C_InitStruct.Timing = 0x30A0A7FB;                     // 保持总线时间
    I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
    I2C_InitStruct.DigitalFilter = 0U;
    I2C_InitStruct.OwnAddress1 = 0U;
    I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
    I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
    LL_I2C_Init(I2C1, &I2C_InitStruct);
}

/**
  * @brief  I2Cx 写数据
  *
  * @note   无
  * @param I2Cx             I2C 实例
  * @param salveAddress     I2C 从机地址
  * @param regAddress       待操作的寄存器地址
  * @param dat              待写入的数据
  * @retval 无
  */
void PD_H_I2C_WriteBytes(I2C_TypeDef *I2Cx, uint8_t salveAddress, uint8_t regAddress, uint8_t *dat, uint8_t len) {
    if (len == 0) return;
    while (LL_I2C_IsActiveFlag_BUSY(I2Cx) == SET);       // 等待总线空闲
    LL_I2C_HandleTransfer(I2Cx,                          // 配置写模式
                          salveAddress,
                          LL_I2C_ADDRSLAVE_7BIT,
                          2,
                          LL_I2C_MODE_AUTOEND,
                          LL_I2C_GENERATE_START_WRITE);
    // 写地址
    while (LL_I2C_IsActiveFlag_TXIS(I2Cx) == RESET);      // 等待发送数据寄存器空
    LL_I2C_TransmitData8(I2Cx, regAddress);         // 写入要操作的地址
    // 写数据
    while (len--) {
        while (LL_I2C_IsActiveFlag_TXIS(I2Cx) == RESET);  // 等待发送数据寄存器空
        LL_I2C_TransmitData8(I2Cx, *dat++);         // 写入数据
    }
    // 停止信号
    while (LL_I2C_IsActiveFlag_STOP(I2Cx) == RESET);      // 等待停止信号
}

/**
  * @brief  I2Cx 读数据
  *
  * @note   无
  * @param I2Cx             I2C 实例
  * @param salveAddress     I2C 从机地址
  * @param regAddress       待操作的寄存器地址
  * @param dat              读取数据保存数组
  * @param len              待读取的数据长度
  * @retval 无
  */
void PD_H_I2C_ReadBytes(I2C_TypeDef *I2Cx, uint8_t salveAddress, uint8_t regAddress, uint8_t *dat, uint8_t len) {
    if (len == 0) return;
    while (LL_I2C_IsActiveFlag_BUSY(I2Cx) == SET);  // 等待总线空闲
    LL_I2C_HandleTransfer(I2Cx,                     // 配置写模式
                          salveAddress,
                          LL_I2C_ADDRSLAVE_7BIT,
                          1,
                          LL_I2C_MODE_AUTOEND,
                          LL_I2C_GENERATE_START_WRITE);
    // 移动寄存器地址指针
    while (LL_I2C_IsActiveFlag_TXIS(I2Cx) == RESET);// 等待发送数据寄存器空
    LL_I2C_TransmitData8(I2Cx, regAddress);   // 写入要读取的地址
    // 读地址
    LL_I2C_HandleTransfer(I2Cx,                     // 配置读模式
                          salveAddress,
                          LL_I2C_ADDRSLAVE_7BIT,
                          1,
                          LL_I2C_MODE_AUTOEND,
                          LL_I2C_GENERATE_START_READ);
    // 读取数据
    while (len--) {
        while (LL_I2C_IsActiveFlag_RXNE(I2Cx) == RESET);// 等待接收数据寄存器非空
        *dat++ = LL_I2C_ReceiveData8(I2Cx);             // 读取数据
    }

    while (LL_I2C_IsActiveFlag_STOP(I2Cx) == RESET);    // 等待停止信号
}


/** endregion Public Definition Region */


/** region Private Definition Region */

/** endregion Private Definition Region */
